<?php

namespace plugin\voice\app\modelHandler;


use plugin\voice\app\model\Apikey;
use plugin\voice\app\service\Azure;
use plugin\voice\app\service\ChatGpt;
use plugin\voice\app\service\Common;
use plugin\voice\app\service\Setting;
use Throwable;

class Gpt3 extends Base
{
    protected $isUserApikey = false;

    protected $request;
    protected $useAzure = false;

    public function __construct($request = null)
    {
        $this->request = $request;
        $model = $request ? $request->post('model') : '';
        if ($request && $authorization = $request->header('Authorization')) {
            $this->apikey = explode(' ', $authorization)[1] ?? '';
            $this->isUserApikey = true;
        } else {
            // 尝试用azure替换openai接口
            $azureSetting = Azure::getSetting();
            if ($azureSetting['enable']??false) {
                $map = json_decode($azureSetting['map'], true);
                if (isset($map[$model])) {
                    $this->api = $azureSetting['api_host'];
                    $this->useAzure = true;
                    $this->path = "/openai/deployments/{$map[$model]}/chat/completions?api-version=2023-05-15";
                    $this->apikey = $azureSetting['apikey1'] ?? $azureSetting['apikey2'] ?? '';
                    return;
                }
            }
        }

        $modelType = strtolower(substr(strrchr(get_class($this), '\\'), 1));
        if (!in_array($modelType, ['gpt3', 'gpt4', 'dalle'])) {
            $modelType = 'gpt3';
        }
        $this->api = ChatGpt::getSetting("{$modelType}_api_host");
        
    }

    public function buildData($data)
    {
        [$model, $messages, $temperature, $stream] = $data;
        $path = $this->path;
        [$schema, $host] = $this->getApi();
        $apiKey = $this->getApiKey('gpt3');
        if (strpos($model, 'gpt') === 0 && $mask = Setting::getSetting('gpt_mask')) {
            $version = '3.5';
            if (preg_match('/\d+(\.\d+)?/', $model, $matches)) {
                $version = $matches[0];
            }
            $mask = str_replace('{version}', $version, $mask);
            if ($messages[0]['role'] !== 'system') {
                $messages = array_merge([[
                    'role' => 'system',
                    'content' => $mask,
                ]], $messages);
            } else {
                $messages[0]['content'] = "$mask\n" . $messages[0]['content'];
            }
        }
        $data = [
            'model' => $model,
            'temperature' => $temperature,
            'stream' => (bool)$stream,
            'messages' => $messages,
        ];
        // gpt-4-vision 默认的max_tokens很低，这里需要前端传递
        if (strpos($model, 'gpt-4-vision') === 0) {
            $data['max_tokens'] = $this->request->post('max_tokens', 2000);
        }
        $body = json_encode($data);
        $bodyLength = strlen($body);
        $authorization = $this->useAzure ? "api-key: $apiKey\r\n" : "Authorization: Bearer $apiKey\r\n";
        return "POST $path HTTP/1.1\r\n" .
            "Host: $host\r\n" .
            "Connection: close\r\n" .
            "Content-Type: application/json\r\n" .
            $authorization .
            "Accept: text/event-stream\r\n" .
            "Content-Length: $bodyLength\r\n\r\n" .
            $body;
    }

    public function formatResponse($buffer)
    {
        if ($buffer && $buffer[0] === '{') {
            if (!$this->isUserApikey) {
                $this->checkApiKeyAvailable($buffer);
            }
            return $buffer;
        }
        $chunks = explode("\n", $buffer);
        $content = '';
        foreach ($chunks as $chunk) {
            if ($chunk === "") {
                continue;
            }
            $chunk = trim(substr($chunk, 6));
            if ($chunk === "" || $chunk === "[DONE]") {
                continue;
            }
            try {
                $data = json_decode($chunk, true);
                if (isset($data['error'])) {
                    $content .= $data['error']['message'] ?? "";
                } else {
                    $content .= $data['choices'][0]['delta']['content'] ?? "";
                }
            } catch (Throwable $e) {
                echo $e;
            }
        }
        return $content;
    }

    /**
     * 获取apikey
     *
     * @return mixed
     */
    public function getApiKey()
    {
        echo '11ababab1??';
        if ($this->apikey) {
            return $this->apikey;
        }
        $request = \request();
        if ($request && $authorization = $request->header('Authorization')) {
            $this->apikey = explode(' ', $authorization)[1] ?? '';
        } else {
            $this->apikey = $this->getApiKeyFromDb('gpt3');
        }
        return $this->apikey;
    }

    /**
     * 从数据库中获取一条可用apikey
     *
     * @return string
     */
    public function getApiKeyFromDb($modelType = 'gpt3')
    {
        echo '???222';
        try {
            $where = ['state'=> 0, 'suspended' => 0, $modelType => 1];
            $item = Apikey::where($where)->orderBy('last_message_at')->orderBy('id')->first();
            if (!$item) {
                unset($where['suspended']);
                $item = Apikey::where($where)->orderBy('last_message_at')->orderBy('id')->first();
                if (!$item) {
                    return '';
                }
            }
            $item->last_message_at = date('Y-m-d H:i:s');
            $item->message_count++;
            $item->save();
            return $item->apikey ?: '';
        } catch (Throwable $exception) {}
        return '';
    }

    /**
     * 通过关键字检测apikey是否可用
     *
     * @param $errorMessage
     * @return void
     */
    protected function checkApiKeyAvailable($errorMessage)
    {
        if (!strpos($errorMessage, 'error')) {
            return;
        }
        // 账号被禁用关键字
        $unavailableKeyWords = ['account_deactivated', 'billing_not_active', 'invalid_api_key', 'insufficient_quota'];
        $disabled = false;
        foreach($unavailableKeyWords as $word) {
            if (strpos($errorMessage, $word) !== false) {
                $disabled = true;
                break;
            }
        }
        // 查找apikey的记录
        $item = Apikey::where('apikey', $this->apikey)->first();
        if ($item) {
            // 标记不可用
            if ($disabled) {
                $item->state = 1;
                $item->suspended = 1;
            }
            // 记录错误相关信息
            $item->last_error = $errorMessage;
            $item->error_count = $item->error_count + 1;
            $item->save();
        }
    }

}